package com.woniu.adapter;

import com.alibaba.fastjson.JSON;
import com.woniu.inlet.web.vo.UserFundAndGoldVo;
import com.woniu.outlet.mysql.dao.PersonalIncomeDao;
import com.woniu.outlet.mysql.po.*;
import com.woniu.outlet.redis.CodeOneRedisDao;
import com.woniu.outlet.redis.po.DailyCodeTOne;
import com.woniu.service.domain.UserFundAndGoldDto;
import com.woniu.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.apache.commons.lang.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class PersonalIncomeAdapter {

    @Autowired
    private PersonalIncomeDao personalIncomeDao;

    @Autowired
    private CodeOneRedisDao redisDao;

    @Autowired
    private HoldProductAdapter holdProductAdapter;

    @Autowired
    private ProductSellAdapter sellAdapter;


    public Long insert(ProductBuyMySQL buy) {
        //先判断用户持有的产品是否存在，如果存在就追加份额
        holdProductAdapter.addHoldProduct(buy);
        //买入的本金
        BigDecimal totalPrincipal = new BigDecimal(buy.getBuyAmount().toString()).subtract(new BigDecimal(buy.getBuyCost()));
        //得到预计开始算收益的时间
        String startIncomeDate = DateUtil.getIncomeDate();
        DailyCodeTOne tOne = redisDao.findById(buy.getCodeId()).orElseThrow(NullPointerException::new);
        PersonalIncomeMySQL personalIncomeMySQL = PersonalIncomeMySQL.builder()
                .userId(buy.getUserId())
                .codeId(buy.getCodeId())
                .productType(buy.getProductType())
                .holdShare(new BigDecimal(buy.getBuyShare()))
                .todayValuation(tOne.getTodayValuation().toString())
                .totalPrincipal(totalPrincipal)
                .totalIncome(new BigDecimal(0))
                .confirmDate(DateUtil.getSendDate())
                .startIncomeDate(startIncomeDate)
                .revocationShare(new BigDecimal("0"))
                .status(-1)
                .build();
        log.debug("个人收益表：插入数据，本金为{}，份额为{}，预计算收益时间{}", totalPrincipal, buy.getBuyShare(), startIncomeDate);
        personalIncomeDao.insert(personalIncomeMySQL);
        return personalIncomeMySQL.getId();
    }

    public PersonalIncomeMySQL findPersonalIncomeMySQLById(Long id) {
        return personalIncomeDao.selectByPrimaryKey(id);
    }

    /**
     * 得到所有开始算收益的单子,当前日期与预期日期一致就开始生成收益
     */
    public List<PersonalIncomeMySQL> getStartIncome() {
        PersonalIncomeMySQLExample example = new PersonalIncomeMySQLExample();
        PersonalIncomeMySQLExample.Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(-1);
        return personalIncomeDao.selectByExample(example);
    }

    /**
     * 第一次算收益成功后，修改状态为0，正常，开始进入每日算收益阶段
     */
    public void updateStatus_1To0(PersonalIncomeMySQL mySQL) {
        mySQL.setStatus(0);
        personalIncomeDao.updateByPrimaryKey(mySQL);
    }

    /**
     * 得到所有状态为0，即可以算收益的单子
     *
     * @return
     */
    public List<PersonalIncomeMySQL> getAllStatus_0() {
        PersonalIncomeMySQLExample example = new PersonalIncomeMySQLExample();
        PersonalIncomeMySQLExample.Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(0);
        return personalIncomeDao.selectByExample(example);
    }

    public void updateIncome(PersonalIncomeMySQL p) {
        personalIncomeDao.updateByPrimaryKey(p);
    }

    /**
     * 得到用户所有的基金持有单子
     *
     * @param userId
     * @return
     */
    public UserFundAndGoldVo findUserFundProduct(Long userId) {
        return getUserProductByType(userId, "fund");
    }

    /**
     * 得到用户所有的黄金持有单子
     *
     * @param userId
     * @return
     */
    public UserFundAndGoldVo findUserGoldProduct(Long userId) {
        return getUserProductByType(userId, "gold");
    }

    /**
     * 计算总体
     *
     * @param userId
     * @param type
     * @return
     */
    public UserFundAndGoldVo getUserProductByType(Long userId, String type) {
        UserFundAndGoldVo vo = new UserFundAndGoldVo();
        //总资产
        BigDecimal totalAssets = new BigDecimal("0");
        //本金总额
        BigDecimal totalPrincipal = new BigDecimal("0");
        //总份额
        BigDecimal totalShare = new BigDecimal("0");
        //总收益
        BigDecimal totalIncome = new BigDecimal("0");
        //所有基金数量
        int allCount = 0;
        //产生收益的基金数量
        int productIncomeCount = 0;
        //等待收益基金数量
        int waitIncomeCount = 0;
        List<UserFundAndGoldDto> lists = new ArrayList<>();
        List<HoldProductMySQL> userByType = holdProductAdapter.findUserByType(userId, type);
        if (userByType.size() == 0) {
            vo.setTotalAssets(totalAssets);
            return vo;
        }
        for (HoldProductMySQL h : userByType) {
            totalPrincipal = totalPrincipal.add(h.getTotalPrincipal());
            totalShare = totalShare.add(h.getTotalShare());
            PersonalIncomeMySQLExample example = new PersonalIncomeMySQLExample();
            PersonalIncomeMySQLExample.Criteria criteria = example.createCriteria();
            criteria.andUserIdEqualTo(userId);
            criteria.andCodeIdEqualTo(h.getCodeId());
            List<PersonalIncomeMySQL> incomes = personalIncomeDao.selectByExample(example);
            //单个基金的多条单子
            //持有份额
            BigDecimal holdShare = new BigDecimal("0");
            //每日收益
            BigDecimal dailyIncome = new BigDecimal("0");
            for (PersonalIncomeMySQL p : incomes) {
                holdShare = holdShare.add(p.getHoldShare());
                if (p.getStatus() == 0) {//已收益的单子
                    productIncomeCount++;
                }
                dailyIncome = dailyIncome.add(p.getTotalIncome());
                allCount++;
            }
            DailyCodeTOne tOne = redisDao.findById(h.getCodeId()).orElseThrow(NullPointerException::new);
            UserFundAndGoldDto dto = UserFundAndGoldDto.builder()
                    .codeId(h.getCodeId())
                    .name(tOne.getName())
                    .dayGrowth(tOne.getDayGrowth())
                    .holdShare(holdShare)
                    .dailyIncome(dailyIncome)
                    .build();
            lists.add(dto);
        }
        for (UserFundAndGoldDto d : lists) {
            totalIncome = totalIncome.add(d.getDailyIncome());
        }
        vo.setTotalAssets(totalPrincipal.add(totalIncome));
        vo.setTotalPrincipal(totalPrincipal);
        vo.setTotalIncome(totalIncome);
        vo.setTotalShare(totalShare);
        vo.setProductIncomeCount(productIncomeCount);
        vo.setWaitIncomeCount(allCount - productIncomeCount);
        vo.setLists(lists);
        return vo;
    }


    /**
     * 得到总收益
     *
     * @param userId
     * @param codeId
     */
    public BigDecimal getTotalIncome(Long userId, String codeId) {
        PersonalIncomeMySQLExample example = new PersonalIncomeMySQLExample();
        PersonalIncomeMySQLExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        criteria.andCodeIdEqualTo(codeId);
        criteria.andStatusEqualTo(0);
        List<PersonalIncomeMySQL> ps = personalIncomeDao.selectByExample(example);
        BigDecimal totalIncome = new BigDecimal("0");
        for (PersonalIncomeMySQL p : ps) {
            totalIncome = totalIncome.add(p.getTotalIncome());
        }
        return totalIncome;
    }

    public String getUserAllFundOrGold(Long userId, String codeId, String sellShare) {
        BigDecimal share = new BigDecimal(sellShare);
        String productType = "";
        List<PersonalIncomeMySQL> ps = getHoldFundOrGold(userId, codeId);
        LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        int count = 0;
        //用于撤销，单子id和对应减少的份额
        List<String> incomeIds = new ArrayList<>();
        List<String> revocationShares = new ArrayList<>();
        //计算要卖出的份额包含哪些单子
        for (PersonalIncomeMySQL p : ps) {
            productType = p.getProductType();
            count++;
            int i = p.getHoldShare().compareTo(share);
            if (i == 0) {//前后相等
                //修改可撤销份额,表示已卖出，将被删除
                sellShare(map, incomeIds, revocationShares, p);
                break;
            }
            if (i < 0) {//前小于后
                BigDecimal subtract = share.subtract(p.getHoldShare());
                share = subtract;
                //不够，进入下一轮
                sellShare(map, incomeIds, revocationShares, p);
            }
            if (i > 0) {//前大于后
                //修改份额，可卖出的份额只有这么多了
                p.setRevocationShare(share);
                addMap(map, p);
                p.setRevocationShare(new BigDecimal("0"));
                p.setHoldShare(p.getHoldShare().subtract(share));
                personalIncomeDao.updateByPrimaryKey(p);
                incomeIds.add(p.getId().toString());
                revocationShares.add(share.toString());
                break;
            }
        }
        log.debug("卖出的单子数{}", count);
        String incomeId = StringUtils.join(incomeIds.toArray(), "-");
        String revocationShare = StringUtils.join(revocationShares.toArray(), "-");
        String id = sellAdapter.addSellOrder(userId, productType, codeId, sellShare, incomeId, revocationShare);
        log.debug("生成卖出记录");
        map.add("sell", id);
        return JSON.toJSONString(map);
    }

    /**
     * 得到code的持有单子
     * @param userId
     * @param codeId
     * @return
     */
    public List<PersonalIncomeMySQL> getHoldFundOrGold(Long userId, String codeId) {
        PersonalIncomeMySQLExample example = new PersonalIncomeMySQLExample();
        PersonalIncomeMySQLExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        criteria.andCodeIdEqualTo(codeId);
        criteria.andRevocationShareEqualTo(new BigDecimal("0"));
        example.setOrderByClause("confirm_date ASC");
        return personalIncomeDao.selectByExample(example);
    }

    /**
     * 修改份额归0的单子状态
     * @param map
     * @param incomeIds
     * @param revocationShares
     * @param p
     */
    public void sellShare(LinkedMultiValueMap<String, String> map, List<String> incomeIds, List<String> revocationShares, PersonalIncomeMySQL p) {
        p.setRevocationShare(p.getHoldShare());
        addMap(map, p);
        p.setRevocationShare(new BigDecimal("-1"));
        revocationShares.add(p.getHoldShare().toString());
        p.setHoldShare(new BigDecimal("0"));
        personalIncomeDao.updateByPrimaryKey(p);
        incomeIds.add(p.getId().toString());
    }

    public void addMap(LinkedMultiValueMap<String, String> map, PersonalIncomeMySQL p) {
        //第一个是codeId,第二个是卖出份额，第三个是确认份额的日期，
        //第四个是userId，第五个是用户本金，第六个是产品类型
        map.add(p.getId().toString(), p.getCodeId());
        map.add(p.getId().toString(), p.getRevocationShare().toString());
        map.add(p.getId().toString(), p.getConfirmDate());
        map.add(p.getId().toString(), p.getUserId().toString());
        map.add(p.getId().toString(), p.getTotalPrincipal().toString());
        map.add(p.getId().toString(), p.getProductType());
    }

    /**
     * 撤销订单，将份额还原
     *
     * @param incomeId
     * @param revocationShare
     */
    public void revocationShares(String incomeId, String revocationShare) {
        PersonalIncomeMySQL mySQL = personalIncomeDao.selectByPrimaryKey(Long.valueOf(incomeId));
        mySQL.setHoldShare(mySQL.getHoldShare().add(new BigDecimal(revocationShare)));
        mySQL.setRevocationShare(new BigDecimal("0"));
        mySQL.setStatus(-1);//代表撤销的订单
        personalIncomeDao.updateByPrimaryKey(mySQL);
    }

    /**
     * 删除持有份额为0的订单
     *
     * @param pid
     */
    public void delIncomeOrder(Long pid) {
        PersonalIncomeMySQL mySQL = personalIncomeDao.selectByPrimaryKey(pid);
        int count = 0;
        if (mySQL.getHoldShare().compareTo(new BigDecimal("0")) == 0) {
            personalIncomeDao.deleteByPrimaryKey(pid);
            count++;
        }
        log.debug("卖光的收益单子有{}条", count);
    }
}
